﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading;
using VRacer.Packets;

namespace VRacer.Connection
{
    /// <summary>
    /// Klasa odpowiedzialna za nasluchiwanie nadchodzacych pakietow. Startuje nowy watek ktory co 200ms sprawdza czy na gniezdzie jest 8 bajtow(naglowek pakietu) jesli tak to wczytuje go i pobiera instancje klasy impementujacej go z PacketFactory.
    /// Nastepnie czeka az na gniezdzie pojawia sie kolejne bajty pakietu. Jesli juz sa to przekazuje wczytanie do pakietu po czym wywoluje OnPacketReceived.
    /// </summary>
    public class Receiver : IDisposable
    {
        /// <summary>
        /// Wystepuje gdy otrzymano nowy pakiet
        /// </summary>
        public event PacketReceivedHandler PacketReceived;

        protected const int SLEEPTIME = 200;

        protected ClientConnection _connection;
        protected BinaryReader _reader;
        protected Thread _thread;

        public Receiver(ClientConnection connection)
        {
            _connection = connection;
            _connection.Connected += _connection_Connected;
            _thread = new Thread(new ThreadStart(checkAndProcessQueue));
            _thread.IsBackground = true;
        }

        void _connection_Connected(object sender, EventArgs e)
        {
            _reader = new BinaryReader(_connection.Stream);
            if (!_thread.IsAlive)
                _thread.Start();

            _connection.Connected -= _connection_Connected;
            _connection.Disconnected += _connection_Disconnected;
        }

        void _connection_Disconnected(object sender, EventArgs e)
        {
            //_thread.Abort();
            _reader = null;
            _connection.Connected += _connection_Connected;
            _connection.Disconnected -= _connection_Disconnected;
        }

        /// <summary>
        /// Polaczenie dla ktorego nasluchujemy
        /// </summary>
        /// <value>The connection.</value>
        private ClientConnection Connection
        {
            get { return _connection; }
        }

        /// <summary>
        /// Sprawdza czy nie ma danych na gniezdzie
        /// </summary>
        private void checkAndProcessQueue()
        {
            try
            {
                while (true)
                {
                    //jesli NIE jest dostepny header to czekaj dalej
                    if (!_connection.IsActive || _connection.Socket.Available < 8)
                    {
                        Thread.Sleep(SLEEPTIME);
                        continue;
                    }

                    int type = _reader.ReadInt32();
                    int length = _reader.ReadInt32();
                    PacketBase packet = PacketFactory.Get(type);
                    packet.Length = length;

                    //czekaj az bedzie dostepny caly pakiet
                    while(!_connection.IsActive || _connection.Socket.Available < length)
                        Thread.Sleep(SLEEPTIME);

                    packet.Read(_reader);
                    OnPacketReceived(packet);
                }
            }
            catch (ThreadAbortException) { }
            catch (Exception e) { _connection.Disconnect(e); }
        }

        protected virtual void OnPacketReceived(PacketBase packet)
        {
            if (PacketReceived != null)
                PacketReceived(this, packet);
        }

        #region IDisposable Members

        public void Dispose()
        {
            throw new NotImplementedException();
        }

        #endregion
    }
}
